#include <iostream>
#include <stdio.h> /* standard I/O routines */
#include <string.h>
#include <stdlib.h>

#ifdef WIN32
// add windows include
#else
#include <pthread.h> /* pthread functions and data structures */
#include <unistd.h>
#endif

// log framework
#include "cppTutorLog.h"
extern CcppTutorLog Log;

#define NUM_EMPLOYEES 2 /* size of each array. */

/* global mutex for our program. assignment initializes it */
#ifdef WIN32
#else
pthread_mutex_t a_mutex = PTHREAD_MUTEX_INITIALIZER;
#endif

struct employee 
{
    int number;
    int id;
    char first_name[20];
    char last_name[30];
    char department[30];
    int room_number;
};
/* global variable - our employees array, with 2 employees */
struct employee employees[] = 
{
    { 1, 12345678, "danny", "cohen", "Accounting", 101},
    { 2, 87654321, "moshe", "levy", "Programmers", 202}
};

/* global variable - employee of the day. */
struct employee employee_of_the_day;

/* function to copy one employee struct into another */
void copy_employee(struct employee* from, struct employee* to)
{
    int rc; /* contain mutex lock/unlock results */
    
    /* lock the mutex, to assure exclusive access to 'a' and 'b'. */
    #ifdef WIN32
    // add windows mutex
    #else
    rc = pthread_mutex_lock(&a_mutex);
    #endif
    to->number = from->number;
    to->id = from->id;
    strcpy(to->first_name, from->first_name);
    strcpy(to->last_name, from->last_name);
    strcpy(to->department, from->department);
    to->room_number = from->room_number;
    
    /* unlock mutex */
    #ifdef WIN32
    #else
    rc = pthread_mutex_unlock(&a_mutex);
    #endif
}

/* function to be executed by the variable setting threads thread */
void* mutex_example_do_loop(void* data)
{
    Log << "ENTER : mutex_example_do_loop ()" << endl;
    
    int my_num = *((int*)data); /* thread identifying number */
    while (1) 
    {
        /* set employee of the day to be the one with number 'my_num'. */
        copy_employee(&employees[my_num-1], &employee_of_the_day);
    }
    
    Log << "EXIT  : mutex_example_do_loop ()" << endl;
}

int mutexDemo()
{
    Log << "ENTER : mutexDemo ()" << endl;
    int i;                  /* loop counter */
    int thr_id1;            /* thread ID for the first new thread */
    int thr_id2;            /* thread ID for the second new thread */
    int num1 = 1;           /* thread 1 employee number */
    int num2 = 2;           /* thread 2 employee number */

#ifdef WIN32
    // windows thread identifier
#else
    pthread_t p_thread1;    /* first thread's structure */
    pthread_t p_thread2;    /* second thread's structure */
#endif

    struct employee eotd;   /* local copy of 'employee of the day'. */
    struct employee* worker; /* pointer to currently checked employee */
    
    /* initialize employee of the day to first 1. */
    copy_employee(&employees[0], &employee_of_the_day);
    
#ifdef WIN32
    // windows thread create
#else
    /* create a new thread that will execute 'mutex_example_do_loop()' with '1' */
    thr_id1 = pthread_create(&p_thread1, NULL, mutex_example_do_loop, (void*)&num1);
    
    /* create a second thread that will execute 'mutex_example_do_loop()' with '2' */
    thr_id2 = pthread_create(&p_thread2, NULL, mutex_example_do_loop, (void*)&num2);
#endif
    /* run a loop that verifies integrity of 'employee of the day' many */
    /* many many times..... */
    for (i=0; i<60000; i++) 
    {
        /* save contents of 'employee of the day' to local 'worker'. */
        copy_employee(&employee_of_the_day, &eotd);
        worker = &employees[eotd.number-1];
        /* compare employees */
        if (eotd.id != worker->id) {
        printf("mismatching 'id' , %d != %d (loop '%d')\n",
        eotd.id, worker->id, i);
        exit(0);
        }
        if (strcmp(eotd.first_name, worker->first_name) != 0) 
        {
            printf("mismatching 'first_name' , %s != %s (loop '%d')\n",
            eotd.first_name, worker->first_name, i);
            exit(0);
        }
        if (strcmp(eotd.last_name, worker->last_name) != 0) 
        {
            printf("mismatching 'last_name' , %s != %s (loop '%d')\n",
            eotd.last_name, worker->last_name, i);
            exit(0);
        }
        if (strcmp(eotd.department, worker->department) != 0) 
        {
            printf("mismatching 'department' , %s != %s (loop '%d')\n",
            eotd.department, worker->department, i);
            exit(0);
        }
        if (eotd.room_number != worker->room_number) 
        {
            printf("mismatching 'room_number' , %d != %d (loop '%d')\n",
            eotd.room_number, worker->room_number, i);
            exit(0);
        }
    }
    printf("Glory, employees contents was always consistent\n");
    
    Log << "EXIT  : mutexDemo ()" << endl;
    return 0;
}
